// @coding: utf8

#include <iostream>
#include <cstdint>
#include <vector>
#include <algorithm>
#include <cassert>
#include <ctime>
#include <random>
#include <chrono>

/**
 * 实现思路类: (忽略暴力枚举)
 * 1.先对牌组新分析，统计好值\花色维度的直方图
 * 2.对这个直方图进行分析，尝试获取牌型最大的组合
 */

int ALL_CARDS[] = {
    0x102,0x103,0x104,0x105,0x106,0x107,0x108,0x109,0x10a,0x10b,0x10c,0x10d,0x10e, //分别代表方块2,3,4,5,6,7,8,9,10,J,Q,K,A
    0x202,0x203,0x204,0x205,0x206,0x207,0x208,0x209,0x20a,0x20b,0x20c,0x20d,0x20e, //分别代表梅花2,3,4,5,6,7,8,9,10,J,Q,K,A
    0x302,0x303,0x304,0x305,0x306,0x307,0x308,0x309,0x30a,0x30b,0x30c,0x30d,0x30e, //分别代表红桃2,3,4,5,6,7,8,9,10,J,Q,K,A
    0x402,0x403,0x404,0x405,0x406,0x407,0x408,0x409,0x40a,0x40b,0x40c,0x40d,0x40e, //分别代表黑桃2,3,4,5,6,7,8,9,10,J,Q,K,A]
};



// 花色
enum ECardColor : int
{
    kHeart = 1,
    kSpade = 2,
    kClub = 3,
    kDiamond = 4,
};

// 牌型
enum EPokerHandType : int
{
    kInvalid = 0,  // 无效类型
    kRoyalFlush = 1,  // 皇家同花顺
    kStraigthFlush = 2, // 同花顺
    kFourOfAKind = 3, // 金刚
    kFullHouse = 4,  // 葫芦
    kFlush = 5,  // 同花
    kStraigth = 6,  // 顺子
    kThreeOfAKind = 7,  // 三条
    kTwoPair = 8,  // 两队
    kOnePair = 9,  // 一对
    kHighCard = 10,  // 高牌
};

// @brief 获取指定花色
inline int get_color(int card)
{
    return card >> 8;
}

// @brief 获取牌值
inline int get_value(int card)
{
    return card & 0x00ff;
}

// @brief 比较两张扑克:  left < right
inline bool compare_card_lesser(int left, int right)
{
    auto left_value = get_value(left);
    auto right_value = get_value(right);
    if (left_value < right_value)
        return true;
    else
    {
        if (left_value == right_value)
        {
            if (get_color(left) < get_color(right))
                return true;
        }

    }
    return false;
}

/// @brief 从指定牌组中拷贝不重复且值为value的牌到out中
/// @param cards 预期按照升序排列，这样获取到的永远是最大的一组数据
/// @param value
/// @param cnt
/// @param out
/// @return
bool copy_n_with_card_value(const std::vector<int>& cards, int value, int cnt, std::vector<int>& out)
{
    if (cnt <= 0)
        return false;
    for (auto iter = cards.rbegin(); iter != cards.rend(); ++iter)
    {
        if (get_value(*iter) == value)
        {
            out.push_back(*iter);
            --cnt;
        }
        if (cnt == 0)
            return true;
    }
    return true;
}

/// @brief 从牌组中赋值指定花色的指定数量牌
/// @param cards
/// @param color
/// @param cnt
/// @param out
/// @return
bool copy_n_with_card_color(const std::vector<int>& cards, int color, int cnt, std::vector<int>& out)
{
    if (cnt <= 0)
        return false;
    for (auto iter = cards.rbegin(); iter != cards.rend(); ++iter)
    {
        if (get_color(*iter) == color)
        {
            out.push_back(*iter);
            --cnt;
        }
        if (cnt == 0)
            return true;
    }
    return false;
}

// @brief 从给定的牌组中获取最优手牌
std::pair<int, std::vector<int>> get_max_hand_cards(const std::vector<int>& input)
{
    auto cards = input;
    // 需求明确为7选5
    if (cards.size() != 7)
        return std::make_pair(EPokerHandType::kInvalid, std::vector<int>{});
    std::sort(cards.begin(), cards.end(), compare_card_lesser);
    std::vector<int> result;
    std::size_t cnt = cards.size();
    int line_cnt = 1;        // 当前连续数量
    int line_start_pos = cnt - 1;  // 当前连续开始位子
    int flush_line_cnt = 1;  // 当前同花且连续数量
    int flush_line_start_pos = cnt - 1;
    int same_cnt = 0;
    std::vector<std::size_t> three_start_pos_list;
    std::vector<std::size_t> two_start_pos_list;
    int color_cnt_map[ECardColor::kDiamond + 1] = {0};
    int value_cnt_map[0x10] = {0};
    // 倒过来搜索，先找到的就是目标答案
    for (std::size_t idx = cnt - 1; idx > 0; --idx)
    {
        // 顺子相关检测
        if (get_value(cards[idx]) == get_value(cards[idx-1]) + 1)
        {
            if (line_cnt < 5)
            {
                line_cnt++;
            }
            // 同花顺检测
            if (get_color(cards[idx]) == get_color(cards[idx-1]))
            {
                if (flush_line_cnt < 5)
                    flush_line_cnt++;
            }
            else
            {
                if (flush_line_cnt < 5)
                {
                    flush_line_cnt = 1;
                    flush_line_start_pos = idx - 1;
                }
            }
        }
        else
        {
            // 值相等无需进行调整
            if (get_value(cards[idx]) != get_value(cards[idx-1]))
            {
                if (line_cnt < 5)
                {
                    line_cnt = 1;
                    line_start_pos = idx - 1;
                }
                if (flush_line_cnt < 5)
                {
                    flush_line_cnt = 1;
                    flush_line_start_pos = idx - 1;
                }
            }
        }
        // 花色统计
        color_cnt_map[get_color(cards[idx])]++;
        // 牌值统计
        if (get_value(cards[idx]) == get_value(cards[idx-1]))
        {
            same_cnt++;
        }
        else
        {
            same_cnt = 1;
        }
        value_cnt_map[get_value(cards[idx])]++;
    }
    color_cnt_map[get_color(cards[0])]++;
    value_cnt_map[get_value(cards[0])]++;
    // 如果是5开始的，长度为4的顺子增加额外处理
    if (line_cnt == 4 && get_value(cards[line_start_pos]) == 0x05)
    {
        if (value_cnt_map[0x0e] > 0)
            line_cnt++;
    }
    if (flush_line_cnt == 4 && get_value(cards[flush_line_start_pos]) == 0x05)
    {
        for (auto iter = cards.rbegin(); iter != cards.rend(); ++iter)
        {
            if (get_value(*iter) != 0x0e)
                break;
            if (get_color(cards[flush_line_start_pos]) == get_color(*iter))
                flush_line_cnt++;
        }
    }

    // 尝试获取皇家同花顺或同花顺
    if (flush_line_cnt >= 5)
    {
        for (auto idx = 0; idx < 5; ++idx)
        {
            result.push_back(cards[flush_line_start_pos - idx]);
        }
        if (get_value(cards[flush_line_start_pos]) == 0x0e)
            return std::make_pair(EPokerHandType::kRoyalFlush, result);
        else
            return std::make_pair(EPokerHandType::kStraigthFlush, result);
    }

    // 尝试获取金刚(7张牌不可能存在两组金刚)
    int* iter = std::find_if(value_cnt_map, value_cnt_map + 0x0f + 1, [](const int& cnt) { return cnt >= 4; });
    if (iter != value_cnt_map + 0x0f + 1)
    {
        int value = *iter;
        for (std::size_t idx = 0; idx < cards.size(); ++idx)
        {
            int card = cards[cnt - idx - 1];
            if (get_value(card) == value)
                result.push_back(card);
            else
            {
                if (result.size() < 5)
                    result.push_back(card);
            }
        }
        return std::make_pair(EPokerHandType::kFourOfAKind, result);
    }

    // 尝试获取葫芦
    int three_value = 0;  // 三张的牌值
    std::vector<int> three_cards;
    int two_value = 0;    // 两张的牌值
    for (int idx = 0x0e; idx >= 0x02; --idx)
    {
        if (value_cnt_map[idx] >= 3)
        {
            if (three_value <= 0)
            {
                three_value = idx;
                copy_n_with_card_value(cards, three_value, 3, three_cards);
            }
            else if (two_value < 0)
                two_value = idx;
        }
        else if (value_cnt_map[idx] >= 2)
        {
            if (two_value <= 0)
                two_value = idx;
        }
        // 搜索完成
        if (three_value && two_value)
            break;
    }
    if (three_value && two_value)
    {
        copy_n_with_card_value(cards, two_value, 2, result);
        for (auto card : three_cards)
            result.push_back(card);
        return std::make_pair(EPokerHandType::kFullHouse, result);
    }

    // 同花
    for (int color = ECardColor::kDiamond; color >= ECardColor::kHeart; --color)
    {
        if (color_cnt_map[color] >= 5)
        {
            copy_n_with_card_color(cards, color, 5, result);
            return std::make_pair(EPokerHandType::kFlush, result);
        }
    }

    // 顺子
    if (line_cnt >= 5)
    {
        for (auto idx = 0; idx < 5; ++idx)
        {
            result.push_back(cards[line_start_pos - idx]);
        }
        return std::make_pair(EPokerHandType::kStraigth, result);
    }

    // 三条
    if (three_value > 0)
    {
        for (auto iter = cards.rbegin(); iter != cards.rend(); ++iter)
        {
            if (three_value != *iter)
                result.push_back(*iter);
            if (result.size() >= 2)
                break;
        }
        result.insert(result.end(), three_cards.begin(), three_cards.end());
        return std::make_pair(EPokerHandType::kThreeOfAKind, result);
    }

    // 两队
    bool one_finish = false;
    for (auto value = 0x0e; value >= 0x02; --value)
    {
        if (value_cnt_map[value] >= 2)
        {
            copy_n_with_card_value(cards, value, 2, result);
        }
        else
        {
            if (!one_finish && value_cnt_map[value] >= 1)
            {
                one_finish = true;
                copy_n_with_card_value(cards, value, 1, result);
            }
        }
        if (result.size() >= 5)
            break;
    }
    if (result.size() == 5)
        return std::make_pair(EPokerHandType::kTwoPair, result);

    // 一对
    bool two_finish = false;
    for (auto value = 0x0e; value >= 0x02; --value)
    {
        if (value_cnt_map[value] >= 2)
        {
            two_finish = true;
            copy_n_with_card_value(cards, value, 2, result);
        }
        else
        {
            if (two_finish || (result.size() < 3 && !two_finish) && value_cnt_map[value] >= 1)
            {
                one_finish = true;
                copy_n_with_card_value(cards, value, 1, result);
            }
        }
        if (result.size() >= 5)
            break;
    }
    if (result.size() == 5)
        return std::make_pair(EPokerHandType::kOnePair, result);

    // 高牌：最大的五张
    for (size_t idx = 0; idx < 5; ++idx)
        result.push_back(cards[cnt - idx - 1]);
    return std::make_pair(EPokerHandType::kHighCard, result);
}

int f(const std::vector<int>& self, std::vector<int>& board)
{
    auto result = get_max_hand_cards(self);
    board = result.second;
    return result.first;
}

bool testcase1_1()
{
    std::pair<int, std::vector<int>> result;
    result = get_max_hand_cards(std::vector<int>{0x108,0x109,0x10a,0x10b,0x10c,0x10d,0x10e});
    assert(result.first == EPokerHandType::kRoyalFlush);

    result = get_max_hand_cards(std::vector<int>{1038,1037,1036,1035,1034,1033,1032});
    assert(result.first == EPokerHandType::kRoyalFlush);

    result = get_max_hand_cards(std::vector<int>{0x10e,0x102,0x103,0x104,0x105,0x107,0x108});
    assert(result.first == EPokerHandType::kStraigthFlush);

    assert(EPokerHandType::kStraigthFlush == get_max_hand_cards(std::vector<int>{0x208,0x207,0x109,0x10a,0x10b,0x10c,0x10d}).first);
    assert(EPokerHandType::kStraigthFlush == get_max_hand_cards(std::vector<int>{0x306,0x209,0x10e,0x102,0x103,0x104,0x105}).first);

    assert(EPokerHandType::kFourOfAKind == get_max_hand_cards(std::vector<int>{0x103, 0x203, 0x303, 0x403, 0x207,0x208,0x309}).first);

    assert(EPokerHandType::kFullHouse == get_max_hand_cards(std::vector<int>{0x105, 0x205, 0x305, 0x409, 0x209, 0x10e, 0x307}).first);
    assert(EPokerHandType::kFullHouse == get_max_hand_cards(std::vector<int>{0x108, 0x208, 0x308, 0x102, 0x303, 0x202, 0x209}).first);

    assert(EPokerHandType::kThreeOfAKind == get_max_hand_cards(std::vector<int>{0x108, 0x208, 0x308, 0x104, 0x303, 0x202, 0x209}).first);

    assert(EPokerHandType::kStraigth == get_max_hand_cards(std::vector<int>{0x10a, 0x105, 0x206, 0x307, 0x408, 0x309, 0x408}).first);

    assert(EPokerHandType::kTwoPair == get_max_hand_cards(std::vector<int>{0x106, 0x206, 0x308, 0x408, 0x209, 0x40b, 0x40d}).first);
    assert(EPokerHandType::kOnePair == get_max_hand_cards(std::vector<int>{0x103, 0x203, 0x204, 0x205, 0x206, 0x309, 0x40a}).first);

    assert(EPokerHandType::kHighCard == get_max_hand_cards(std::vector<int>{0x103, 0x20e, 0x204, 0x205, 0x206, 0x309, 0x40a}).first);

    return true;
}

// 额外的测试用例
std::vector<std::pair<int, std::vector<int>>> testdatas;

void init_testdatas()
{
    testdatas.push_back(std::make_pair(EPokerHandType::kRoyalFlush, std::vector<int>{0x10a, 0x10b, 0x10c, 0x10d, 0x10e, 0x109, 0x108}));
    testdatas.push_back(std::make_pair(EPokerHandType::kFourOfAKind, std::vector<int>{0x108, 0x108, 0x108, 0x208, 0x10a, 0x20a, 0x30a}));
    testdatas.push_back(std::make_pair(EPokerHandType::kFourOfAKind, std::vector<int>{0x205, 0x205, 0x205, 0x105, 0x10a, 0x20a, 0x208}));
    testdatas.push_back(std::make_pair(EPokerHandType::kFlush, std::vector<int>{0x102, 0x104, 0x106, 0x108, 0x10a, 0x103, 0x107}));
    testdatas.push_back(std::make_pair(EPokerHandType::kStraigthFlush, std::vector<int>{0x103, 0x104, 0x105, 0x106, 0x207, 0x102, 0x108}));
    testdatas.push_back(std::make_pair(EPokerHandType::kFlush, std::vector<int>{0x206, 0x206, 0x206, 0x108, 0x309, 0x207, 0x205}));
    testdatas.push_back(std::make_pair(EPokerHandType::kTwoPair, std::vector<int>{0x102, 0x102, 0x304, 0x304, 0x206, 0x108, 0x308}));
    testdatas.push_back(std::make_pair(EPokerHandType::kStraigth, std::vector<int>{0x208, 0x208, 0x103, 0x305, 0x407, 0x206, 0x204}));
    testdatas.push_back(std::make_pair(EPokerHandType::kHighCard, std::vector<int>{0x102, 0x304, 0x209, 0x408, 0x30a, 0x103, 0x205}));

}

bool testcase1_2()
{
    for (std::size_t idx = 0; idx < testdatas.size(); ++idx)
    {
        auto testdata = testdatas[idx];
        auto result = get_max_hand_cards(testdata.second);
        if (testdata.first != result.first)
        {
            std::cout << "faild idx=" << idx << std::endl;
            assert(false);
        }
    }

    return 0;
}

int main()
{
    init_testdatas();
    testcase1_1();
    testcase1_2();
    return 0;
}